package com.mspbots.sync.tsheets;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mspbots.api.RestMessageObject;
import com.mspbots.sync.BaseGenericService;
import com.mspbots.sync.model.SheetsTime;
import com.mspbots.sync.model.SheetsUser;
import com.mspbots.sync.tsheets.client.SheetsGetClient;
import com.mspbots.sync.utils.WiseResultBuildUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;

/**
 * com.mspbots.sync.scheduled.tsheets.SheetsSyncService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/7/25
 */
@Log4j2
@Service
public class SheetsSyncService extends BaseGenericService {

    public Mono<Object> manualSync(Integer tenantId) {
        return this.onGetSheetProperties(tenantId)
                .doOnNext(configuration -> {
                    this.autoSyncUsers(configuration);
                    this.autoSyncTimeSheets(configuration);
                })
                .map(configuration -> RestMessageObject.ok("Manual Sync tsheet success"));
    }

    public void autoSyncUsers(SheetsProperties configuration) {
        SheetsGetClient sheetsGetClient = new SheetsGetClient(configuration);
        this.syncUsersProgress(configuration.getTenantId(), sheetsGetClient, 1);
    }

    private void syncUsersProgress(Integer tenantId, SheetsGetClient sheetsGetClient, int page) {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.set("supplemental_data", "no");
        params.set("page", String.valueOf(page));

        sheetsGetClient.getUserRepository(params)
                .subscribe(result -> {
                    Map<String, ObjectNode> users = this.objectMapper.convertValue(result.findPath("results").findPath("users"),
                            new TypeReference<>() {
                            });
                    users.forEach((k, v) -> {
                        SheetsUser sheetsUser = new SheetsUser();
                        sheetsUser.setEmail(v.findValue("email").asText());
                        sheetsUser.setSheetUserId(Long.parseLong(k));
                        sheetsUser.setFirstName(v.findValue("first_name").asText());
                        sheetsUser.setLastName(v.findValue("last_name").asText());
                        sheetsUser.setLastModified(WiseResultBuildUtils.timeCovert(v.findValue("last_modified").asText()));
                        sheetsUser.setTenantId(tenantId);
                        sheetsUser.setSyncTime(LocalDateTime.now());
                        sheetsUser.setExtend(v);

                        this.messagingTemplate.convertAndSend("msp.sheet.user", "sheet-users-callback", sheetsUser);
                    });

                    if (result.findValue("more").asBoolean()) {
                        int pageNow = page + 1;
                        this.syncUsersProgress(tenantId, sheetsGetClient, pageNow);
                    }

                    this.recordExceptionLog(tenantId, 200,
                            "SYNC-THSEET-USER", "Connect Tsheet from sync is connected.");

                }, ex -> this.recordExceptionLog(tenantId, 500,
                        "SYNC-THSEET-USER", ex.getMessage()));
    }

    public void autoSyncTimeSheets(SheetsProperties configuration) {
        SheetsGetClient sheetsGetClient = new SheetsGetClient(configuration);
        this.syncTimeSheetsProgress(configuration.getTenantId(), sheetsGetClient, 1);
    }

    private void syncTimeSheetsProgress(Integer tenantId, SheetsGetClient sheetsGetClient, int page) {
        this.loadRedisValue("sync-time-sheet-" + tenantId)
                .map(startTime -> {
                    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
                    params.set("on_the_clock", "both");
                    params.set("supplemental_data", "no");
                    params.set("modified_since", startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:00'Z'")));
                    params.set("page", String.valueOf(page));
                    return params;
                }).flatMap(sheetsGetClient::getTimeSheetsRepository)
                .subscribe(result -> {
                    Map<String, ObjectNode> timeSheets = this.objectMapper.convertValue(result.findPath("results").findPath("timesheets"),
                            new TypeReference<>() {
                            });

                    timeSheets.forEach((k, v) -> {
                        SheetsTime sheetsTime = WiseResultBuildUtils.buildSheetsTime(tenantId, k, v);
                        this.loadRedisValue("sync-time-sheet-" + tenantId)
                                .subscribe(syncTime -> {
                                    if (syncTime.isBefore(sheetsTime.getLastModified())) {
                                        this.recordRedisValue("sync-time-sheet-" + tenantId,
                                                sheetsTime.getLastModified().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
                                    }
                                });
                        this.messagingTemplate.convertAndSend("msp.wise.sheet.times", "", sheetsTime);
                    });

                    if (result.findValue("more").asBoolean()) {
                        int pageNow = page + 1;
                        log.info("tsheets time page no. {},next page no. {}", page, pageNow);
                        this.syncTimeSheetsProgress(tenantId, sheetsGetClient, pageNow);
                    }
                });
    }

}
